home *** CD-ROM | disk | FTP | other *** search
/ Mac Format 1994 October / Macformat17.cdr / Shareware City / Developers / xlispmac / src / macint.c < prev    next >
Text File  |  1994-06-25  |  30KB  |  982 lines

  1. /* macint.c - macintosh interface routines for xlisp 2.1e */
  2. /* Written by Brian Kendig. */
  3. /* The functions here are only called by macstuff.c. */
  4.  
  5. #include <THINK.h>
  6. #include <Desk.h>
  7. #include <Events.h>
  8. #include <GestaltEQU.h>
  9. #include <LoMem.h>
  10. #include <Memory.h>
  11. #include <Menus.h>
  12. #include <OSEvents.h>
  13. #include <Quickdraw.h>
  14. #include <StandardFile.h>
  15. #include <TextEdit.h>
  16. #include <ToolUtils.h>
  17. #include <Traps.h>
  18. #include <Windows.h>
  19. #include <pascal.h>
  20. #include "macint.h"
  21.  
  22. #define TEXTREC        (*hTERec)  /* the command window text record */
  23. #define TEXTCHAR(i)    ((*(TEXTREC->hText))[i])
  24.  
  25. MenuHandle appleMenu, fileMenu, editMenu, controlMenu;
  26.  
  27. /* command and graphics windows */
  28. WindowPtr        commandWin, graphicsWin;
  29. WindowRecord    commandWinRec, bwGraphicsWinRec;
  30. CWindowRecord    colorGraphicsWinRec;
  31. Boolean            graphicsShown, commandWinResized = false;
  32. Rect            dragRect, sizeRect;
  33.  
  34. int screenWidth, screenHeight;                    /* screen dimensions */
  35. int sHorizontal, sVertical, sWidth, sHeight;    /* command win, split screen */
  36. int gHorizontal, gVertical, gWidth, gHeight;    /* graphics win, split screen */
  37.  
  38. /* output is buffered */
  39. Handle hOutputBuffer = NULL;
  40. int outputBufferLength = 0;
  41.  
  42. /* miscellaneous stuff */
  43. char        recentChar;                            /* the last character typed */
  44. int         cursorPos;                            /* the cursor's position on the line */
  45. short        linesInView;                        /* how many lines are in the window */
  46. CharsHandle    pastedTextH = NULL;                    /* a handle to pasted text */
  47. int            pastedLength = 0;                    /* how many chars there are in the paste buffer */
  48. int            flashTime = 0, cursorBeforeFlash;    /* for flashing cursor when parens match */
  49. int            cmdStart;                            /* where (in text record) the current command starts */
  50.  
  51. TextStyle textStyle[2];  /* styles: bold for user input, plain for output */
  52. enum { plainStyle, boldStyle } currentStyle;
  53.  
  54. int wneImplemented;
  55. EventRecord theEvent;
  56. TEHandle hTERec;
  57. ControlHandle vScroll;
  58. unsigned long startupTicks;
  59. Boolean hasColorQD;
  60.  
  61. short howManyFiles = 0, whichFile = 0;  /* keep track of files opened from Finder */
  62.  
  63. void DoEvent (void);        /* forward */
  64. void FlushOutput (void);    /* forward */
  65.  
  66. static void DoScrollBar (ControlHandle control, short change) {
  67.     /* keep track of the user as he fiddles with the scroll bar */
  68.     /* This routine is called while the user has the mouse down. */
  69.     /* It makes sure the thumb isn't dragged out-of-bounds. */
  70.     short value = GetCtlValue (control), max = GetCtlMax (control);
  71.     long newval = value + change;  /* this is a long in case we try to go past MAX_INT */
  72.     if (newval < 0) newval = 0; else if (newval > max) newval = max;
  73.     SetCtlValue (control, (short) newval);
  74.     if (newval != value) TEScroll (0, (short) (value - newval) * LINEHEIGHT, hTERec);
  75. }
  76.  
  77. static pascal void ScrollProc (ControlHandle control, short thePart) {
  78.     /* for clicks in the scroll bar or arrows; update the window properly */
  79.     short amount;
  80.     WindowPtr window;
  81.  
  82.     if (!thePart) return;
  83.     window = (*control)->contrlOwner;
  84.     switch (thePart) {
  85.         case inUpButton:    amount = -1;                break;
  86.         case inDownButton:    amount = 1;                    break;
  87.         case inPageUp:        amount = -linesInView;        break;
  88.         case inPageDown:    amount = linesInView;        break;
  89.     }
  90.     DoScrollBar (control, amount);
  91. }
  92.  
  93. static pascal Boolean ScrollClikLoop (void) {
  94.     Rect tempRect;
  95.     Point mouse;
  96.     GrafPtr oldPort;
  97.     RgnHandle oldClip;
  98.     short amount = 0;
  99.  
  100.     if (FrontWindow () != commandWin) return false;
  101.  
  102.     GetPort (&oldPort);
  103.     SetPort (commandWin);
  104.     GetClip (oldClip = NewRgn ());
  105.     SetRect (&tempRect, INT_MIN, INT_MIN, INT_MAX, INT_MAX);
  106.     ClipRect (&tempRect);
  107.  
  108.     GetMouse (&mouse);
  109.     if (mouse.v < TEXTREC->viewRect.top)              DoScrollBar (vScroll, -1);
  110.     else if (mouse.v > TEXTREC->viewRect.bottom)    DoScrollBar (vScroll, 1);
  111.  
  112.     SetClip (oldClip);
  113.     DisposeRgn (oldClip);
  114.     SetPort (oldPort);
  115.     return true;
  116. }
  117.  
  118. static Rect SetTERect (void) {
  119.     /* set the dimensions of the text record in its window */
  120.     Rect teRect = commandWin->portRect;
  121.     teRect.right -= SCROLLER_WIDTH;
  122.     InsetRect (&teRect, TEXT_MARGIN, TEXT_MARGIN);
  123.     linesInView = (teRect.bottom - teRect.top) / LINEHEIGHT;
  124.     teRect.bottom = teRect.top + linesInView * LINEHEIGHT;  /* round off */
  125.     return teRect;
  126. }
  127.  
  128. static void SetScrollRect (void) {
  129.     /* set the dimensions of the scroll bar in its window */
  130.     MoveControl (vScroll, commandWin->portRect.right - SCROLLER_WIDTH, -1);
  131.     SizeControl (vScroll, SCROLLER_WIDTH + 1,
  132.             (commandWin->portRect.bottom - commandWin->portRect.top) - (SCROLLER_WIDTH - 2));
  133.  
  134. }
  135.  
  136. static void AdjustCursor (void) {
  137.     /* make the pointer an I-beam iff it's in the text window */
  138.     Point mouse = theEvent.where;
  139.     if (commandWin != FrontWindow ()) {
  140.         SetCursor (&arrow);
  141.         return;
  142.     } else {
  143.         GlobalToLocal (&mouse);
  144.         if (PtInRect (mouse, &(TEXTREC->viewRect))) SetCursor (*GetCursor (iBeamCursor));
  145.         else SetCursor (&arrow);
  146.     }
  147. }
  148.  
  149. void ShowGrafWin (void) {
  150.     /* make the graphics window visible */
  151.     ShowWindow (graphicsWin);
  152.     SelectWindow (graphicsWin);
  153.     SetItem (controlMenu, SHOW_GRAPHICS, "\pHide Graphics");
  154.     AdjustCursor ();
  155.     graphicsShown = true;
  156. }
  157.  
  158. void HideGrafWin (void) {
  159.     /* hide the graphics window */
  160.     HideWindow (graphicsWin);
  161.     SetItem (controlMenu, SHOW_GRAPHICS, "\pShow Graphics");
  162.     graphicsShown = false;
  163. }
  164.  
  165. static short roundup (float x) {  /* a kludge to round up a float to an int */
  166.     if (((int) x) != ((int) (x += 0.5))) x += 0.5;
  167.     return (int) x;
  168. }
  169.  
  170. static void AdjustScrollBar (void) {
  171.     /* adjust the scroll bar to match the position of the text view */
  172.     short oldval = GetCtlValue (vScroll), oldmax = GetCtlMax (vScroll);
  173.     short value, max;
  174.  
  175.     max = TEXTREC->nLines - linesInView;
  176.     if ((TEXTREC->teLength > 0) && (*(*TEXTREC->hText + TEXTREC->teLength - 1) == '\r')) max++;
  177.     if (max < 0) max = 0;
  178.     if (max != oldmax) SetCtlMax (vScroll, max);
  179.     value = roundup ((TEXTREC->viewRect.top - TEXTREC->destRect.top) / LINEHEIGHT);
  180.     if (value < 0) value = 0; else if (value > max) value = max;
  181.     if (value != oldval) SetCtlValue (vScroll, value);
  182. }
  183.  
  184. static void DrawOnlyGrowIcon (WindowPtr window) {  /* doesn't draw scroll bars */
  185.     RgnHandle saveRgn;
  186.     Rect growRect;
  187.  
  188.     growRect = window->portRect;
  189.     growRect.top = growRect.bottom - SCROLLER_WIDTH;
  190.     growRect.left = growRect.right - SCROLLER_WIDTH;
  191.     GetClip (saveRgn = NewRgn ());
  192.     ClipRect (&growRect);
  193.     DrawGrowIcon (window);
  194.     SetClip (saveRgn);
  195.     DisposeRgn (saveRgn);
  196. }
  197.  
  198. static void UpdateCmdWindow (void) {
  199.     long textBottom;
  200.  
  201.     InvalRect (&(commandWin->portRect));
  202.     BeginUpdate (commandWin);
  203.     EraseRect (&(commandWin->portRect));
  204.     FlushOutput ();
  205.     DrawOnlyGrowIcon (commandWin);
  206.     if (commandWinResized) {
  207.         TEXTREC->viewRect = SetTERect ();
  208.         TEXTREC->destRect.right = TEXTREC->viewRect.right;
  209.         TECalText (hTERec);
  210.         SetScrollRect ();
  211.         commandWinResized = false;
  212.     }
  213.  
  214.     TEXTREC->viewRect = SetTERect ();  /* adjust for possible change in height of status line */
  215.  
  216.     textBottom = TEXTREC->destRect.top + (TEXTREC->nLines * LINEHEIGHT);
  217.     if (TEXTREC->destRect.top > TEXTREC->viewRect.top)
  218.         TEScroll (0, (TEXTREC->viewRect.top - TEXTREC->destRect.top), hTERec);
  219.  
  220.     if (TEXTREC->destRect.top < TEXTREC->viewRect.top) {  /* make sure we don't get fractions of lineheights */
  221.         int amountOffTheTop = TEXTREC->viewRect.top - TEXTREC->destRect.top;
  222.         if (amountOffTheTop % LINEHEIGHT) TEScroll (0, amountOffTheTop % LINEHEIGHT, hTERec);
  223.     }
  224.     TEUpdate (&(TEXTREC->viewRect), hTERec);
  225.     AdjustScrollBar ();
  226.     UpdtControl (commandWin, commandWin->visRgn);
  227.     EndUpdate (commandWin);
  228. }
  229.  
  230. void InitMac (void) {
  231.     /* do all the necessary initialization mumbo-jumbo */
  232.  
  233.     {  /* set up memory properly */
  234.         int i;
  235.         if (DefltStack < STACKMIN) SetApplLimit (CurStackBase - STACKMIN);
  236.         MaxApplZone ();
  237.         for (i = 0; i < MASTERS; i++) MoreMasters ();
  238.     }
  239.  
  240.     /* initialize the toolbox */
  241.     InitGraf (&thePort);
  242.     InitFonts ();
  243.     FlushEvents (everyEvent, 0);
  244.     InitWindows ();
  245.     InitMenus ();
  246.     TEInit ();
  247.     InitDialogs (NIL);
  248.     InitCursor ();
  249.  
  250.     /* see if we have WaitNextEvent and Color Quickdraw */
  251.     wneImplemented = (NGetTrapAddress (_WaitNextEvent, ToolTrap) != NGetTrapAddress (_Unimplemented, ToolTrap));
  252.     if (NGetTrapAddress ((short) Gestalt, ToolTrap) != NGetTrapAddress (_Unimplemented, ToolTrap)) {
  253.         long returnCode;
  254.         OSErr err = Gestalt (gestaltQuickdrawVersion, &returnCode);
  255.         hasColorQD = ((err == noErr) && (returnCode >= gestalt8BitQD));
  256.     } else hasColorQD = false;
  257.  
  258.     {  /* set up menus */
  259.         Handle theMenuBar = GetNewMBar (MBAR_RES);
  260.         SetMenuBar (theMenuBar);
  261.         appleMenu = GetMHandle (APPLE_MENU_RES);
  262.         fileMenu = GetMHandle (FILE_MENU_RES);
  263.         editMenu = GetMHandle (EDIT_MENU_RES);
  264.         controlMenu = GetMHandle (CONTROL_MENU_RES);
  265.         AddResMenu (appleMenu, 'DRVR');
  266.         DrawMenuBar ();
  267.     }
  268.  
  269.     /* get the size of the main screen */
  270.     screenWidth  = screenBits.bounds.right  - screenBits.bounds.left;
  271.     screenHeight = screenBits.bounds.bottom - screenBits.bounds.top;
  272.  
  273.     /* create the command and graphics windows */
  274.     commandWin = GetNewWindow (CWINRES, &commandWinRec, (WindowPtr) -1L);
  275.     if (hasColorQD) graphicsWin = GetNewCWindow (GWINRES, &colorGraphicsWinRec, (WindowPtr) -1L);
  276.     else graphicsWin = GetNewWindow (GWINRES, &bwGraphicsWinRec, (WindowPtr) -1L);
  277.     SetPort (commandWin);
  278.  
  279.     /* compute the size of the graphics window in split-screen mode */
  280.     gHorizontal = SCREEN_MARGIN;
  281.     gVertical = MBAR_HEIGHT + TITLEBAR_HEIGHT - 1;
  282.     gWidth = screenWidth - (SCREEN_MARGIN * 2);
  283.     gHeight = GRAFWIN_HEIGHT;
  284.  
  285.     /* compute the size of the command window in split-screen mode */
  286.     sHorizontal = SCREEN_MARGIN;
  287.     sVertical = MBAR_HEIGHT + TITLEBAR_HEIGHT - 1 + SCREEN_MARGIN + GRAFWIN_HEIGHT;
  288.     sWidth = screenWidth - (SCREEN_MARGIN * 2);
  289.     sHeight = screenHeight - MBAR_HEIGHT - TITLEBAR_HEIGHT - (SCREEN_MARGIN * 2) - GRAFWIN_HEIGHT - 1;
  290.  
  291.     /* set up size and drag rects */
  292.     dragRect = (*GetGrayRgn ())->rgnBBox;
  293.     dragRect.left += DRAG_THRESHOLD;
  294.     dragRect.right -= DRAG_THRESHOLD;
  295.     dragRect.bottom -= DRAG_THRESHOLD;
  296.     sizeRect.top = MIN_WIN_HEIGHT;
  297.     sizeRect.left = MIN_WIN_WIDTH;
  298.     sizeRect.bottom = screenBits.bounds.bottom - screenBits.bounds.top;
  299.     sizeRect.right = screenBits.bounds.right - screenBits.bounds.left;
  300.  
  301.     /* setup the font, size and writing mode for the command window */
  302.     TextFont (monaco);
  303.     TextSize (9);
  304.     TextFace (0);
  305.     TextMode (srcCopy);
  306.     textStyle[plainStyle].tsFace = 0;
  307.     textStyle[boldStyle].tsFace = bold;
  308.     currentStyle = plainStyle;
  309.  
  310.     startupTicks = TickCount ();  /* take note of what time we're starting up */
  311.  
  312.     {  /* set up scroll bar */
  313.         Rect scrollRect;
  314.         vScroll = NewControl (commandWin, &scrollRect, "\p", 0, 0, 0, 0, scrollBarProc, NIL);
  315.         SetScrollRect ();
  316.         ShowControl (vScroll);
  317.     }
  318.  
  319.     {  /* set up command text record */
  320.         Rect teRect = SetTERect ();
  321.         hTERec = TEStylNew (&teRect, &teRect);
  322.          TECalText (hTERec);
  323.         TEAutoView (true, hTERec);
  324.         SetClikLoop (ScrollClikLoop, hTERec);
  325.         TEActivate (hTERec);
  326.     }
  327.  
  328.     hOutputBuffer = NewHandle (MAX_BUF);  /* a handle to a buffer for text to be displayed */
  329.  
  330.     HideGrafWin ();
  331.  
  332.     {  /* see if the user launched the app by opening text files from the Finder */
  333.         short doWhat;
  334.         CountAppFiles (&doWhat, &howManyFiles);
  335.         if (doWhat != appOpen) howManyFiles = 0;
  336.     }
  337.  
  338.     UpdateCmdWindow ();
  339.  
  340. }
  341.  
  342. static void SetSelection (short start, short end) {
  343.     TEXTREC->clikStuff = 255;  /* to make sure the caret appears at the start of a line when it should */
  344.     /* see tech note "TextEdit EOL Ambiguity" for more information */
  345.     TESetSelect (start, end, hTERec);
  346. }
  347.  
  348. static void CancelFlash (void) {
  349.     /* cancel the matching-paren flashing */
  350.     if (flashTime) {
  351.         flashTime = 0;
  352.         SetSelection (cursorBeforeFlash, cursorBeforeFlash);
  353.     }
  354. }
  355.  
  356. static void StopPasting (void) {
  357.     /* clean up after finishing a paste */
  358.     pastedLength = 0;
  359.     if (pastedTextH) {
  360.         DisposHandle (pastedTextH);
  361.         pastedTextH = NULL;
  362.     }
  363. }
  364.  
  365. static void DoStyle (int whatStyle) {
  366.     /* set the text to a certain style */
  367.     TESetStyle (doFace, &(textStyle[whatStyle]), false, hTERec);
  368. }
  369.  
  370. static void FlushOutput (void) {
  371.     /* clear out the output buffer, dumping its contents to the window */
  372.     short totalLines, scrollAmount, max;
  373.  
  374.     if (outputBufferLength == 0) return;
  375.     CancelFlash ();
  376.     DoStyle (plainStyle);
  377.     HLock (hOutputBuffer);
  378.     TEInsert (*hOutputBuffer, outputBufferLength, hTERec);
  379.     HUnlock (hOutputBuffer);
  380.     outputBufferLength = 0;
  381.  
  382.     if (TEXTREC->teLength > SCROLLBACK_THRESHHOLD) {  /* make sure TE record isn't too long */
  383.         int i = 1, newLength;
  384.         TEPtr textPtr;
  385.         while ((TEXTREC->teLength - TEXTREC->lineStarts[i]) > (SCROLLBACK_THRESHHOLD - DELETE_BLOCK)) i++;
  386.         i = TEXTREC->lineStarts[i];
  387.         newLength = TEXTREC->teLength - i;
  388.         textPtr = (TEPtr)(*(TEXTREC->hText));
  389.         BlockMove ((Ptr)((long)textPtr + i), textPtr, newLength);
  390.         SetHandleSize (TEXTREC->hText, newLength);
  391.         TEXTREC->destRect.top += LINEHEIGHT;
  392.         TECalText (hTERec);
  393.         TEUpdate (&(TEXTREC->viewRect), hTERec);
  394.     }
  395.     TESelView (hTERec);
  396.     AdjustScrollBar ();
  397. }
  398.  
  399. static void PrepareForInput (void) {
  400.     /* get ready to take input */
  401.     FlushOutput ();
  402.     cmdStart = TEXTREC->selStart;
  403. }
  404.  
  405. static void DeleteRange (void) {
  406.     /* delete the selected range of text, updating cmdStart as necessary */
  407.     if (TEXTREC->selEnd <= cmdStart) return;
  408.     if (TEXTREC->selStart < cmdStart) SetSelection (cmdStart, TEXTREC->selEnd);
  409.     TEDelete (hTERec);
  410. }
  411.  
  412. static void CopyThisLineToEnd (void) {
  413.     /* copy the line the caret is on to the end */
  414.     char *buffer;
  415.     short b, i, caretOffset;
  416.  
  417.     /* first find out exactly where it starts */
  418.     i = TEXTREC->nLines-1;  /* first find which line */
  419.     while (TEXTREC->selStart < TEXTREC->lineStarts[i]) i--;
  420.     while ((i > 0) && ((*(TEXTREC->hText))[TEXTREC->lineStarts[i]-1] != '\r'))
  421.         i--;  /* for wrapped lines */
  422.     i = TEXTREC->lineStarts[i];  /* now zero in on the exact character where it begins */
  423.     while ((TEXTCHAR(i) >= '0') && (TEXTCHAR(i) <= '9')) i++;  /* skip error level */
  424.     if ((TEXTCHAR(i) == '>') && (TEXTCHAR(i+1) == ' ')) i+=2;  /* get rid of leading prompt */
  425.  
  426.     caretOffset = TEXTREC->selStart - i;  /* how many characters in is the caret? */
  427.  
  428.     /* now put the line into the buffer */
  429.     b = 0;
  430.     while ((TEXTCHAR(i+b) != '\r') && (i+b < TEXTREC->teLength)) b++;  /* find the end of the line */
  431.     buffer = (char *) NewPtr (b);
  432.     BlockMove (*TEXTREC->hText + i, buffer, b);
  433.     buffer[b] = '\0';
  434.  
  435.     /* delete whatever's already on the last line */
  436.     SetSelection (cmdStart, TEXTREC->teLength);
  437.     TEDelete (hTERec);
  438.  
  439.     DoStyle (boldStyle);
  440.     TEInsert (buffer, b, hTERec);
  441.     DisposPtr (buffer);
  442.  
  443.     if (caretOffset < 0) caretOffset = b;
  444.     SetSelection (cmdStart + caretOffset, cmdStart + caretOffset);
  445. }
  446.  
  447. static void GoStartOfLine (void) {
  448.     short whichLine = TEXTREC->nLines - 1;  /* look for the caret; start at the end and go up */
  449.     while (TEXTREC->lineStarts[whichLine] > TEXTREC->selStart) whichLine--;
  450.     SetSelection (TEXTREC->lineStarts[whichLine], TEXTREC->lineStarts[whichLine]);
  451.     AdjustScrollBar ();
  452. }
  453.  
  454. static void GoEndOfLine (void) {
  455.     short whichLine = TEXTREC->nLines - 1;  /* look for the caret; start at the end and go up */
  456.     while (TEXTREC->lineStarts[whichLine] > TEXTREC->selStart) whichLine--;
  457.     if (whichLine == TEXTREC->nLines - 1)
  458.         SetSelection (TEXTREC->teLength, TEXTREC->teLength);
  459.     else SetSelection (TEXTREC->lineStarts[whichLine+1] - 1, TEXTREC->lineStarts[whichLine+1] - 1);
  460.     AdjustScrollBar ();
  461. }
  462.  
  463. static void GoBackOneWord (void) {
  464.     short i = TEXTREC->selStart;
  465.     while ((i > 0) && !isalnum (TEXTCHAR(i-1)))    i--;
  466.     while ((i > 0) && isalnum (TEXTCHAR(i-1)))    i--;
  467.     SetSelection (i, i);
  468. }
  469.  
  470. static void GoForwardOneWord (void) {
  471.     short i = TEXTREC->selStart;
  472.     while ((i < TEXTREC->teLength) && !isalnum (TEXTCHAR(i)))    i++;
  473.     while ((i < TEXTREC->teLength) && isalnum (TEXTCHAR(i)))    i++;
  474.     SetSelection (i, i);
  475. }
  476.  
  477. static void EditFreely (void) {
  478.     Boolean done;
  479.     do {
  480.         done = false;
  481.         DoEvent ();
  482.         if (pastedLength > 0) {  /* if there is still text to paste, paste it */
  483.             int i = 0;
  484.             CancelFlash ();
  485.             if (TEXTREC->selStart < cmdStart) StopPasting ();
  486.             else {
  487.                 while ((i < pastedLength) && (((char *)(*pastedTextH))[i] != '\r')) i++;
  488.                 DoStyle (boldStyle);
  489.                 TEInsert (*pastedTextH, i, hTERec);
  490.                 AdjustScrollBar ();
  491.                 if (i < pastedLength) {  /* we were stopped by a carriage return, so eat it */
  492.                     i++;
  493.                     done = true;
  494.                 }
  495.                 pastedLength -= i;
  496.                 if (pastedLength > 0) {
  497.                     BlockMove ((Ptr)((long)(*pastedTextH) + i), *pastedTextH, pastedLength);
  498.                     SetHandleSize (pastedTextH, pastedLength);
  499.                 } else StopPasting ();
  500.             }
  501.         }
  502.         else if (recentChar) {  /* if the last event got us a character, process it */
  503.             int i;
  504.             Boolean wasOnLastLine;
  505.             CancelFlash ();
  506.  
  507.             if ((TEXTREC->selEnd <= cmdStart) && (TEXTREC->selStart != TEXTREC->selEnd)) continue;
  508.             if (TEXTREC->selStart < cmdStart) SetSelection (cmdStart, TEXTREC->selEnd);
  509.             wasOnLastLine = (TEXTREC->selStart >= cmdStart);
  510.  
  511.             if ((recentChar & 0xfc) == 0x1c) {  /* was this an arrow key? */
  512.                 TEXTREC->clikStuff = 255;  /* to make sure the caret appears where it should */
  513.                 TEKey (recentChar, hTERec);
  514.                 AdjustScrollBar ();
  515.                 continue;
  516.             }
  517.             if (!wasOnLastLine) CopyThisLineToEnd ();
  518.             switch (recentChar) {
  519.             case FWDDEL:
  520.                 if (TEXTREC->selStart != TEXTREC->selEnd) DeleteRange ();
  521.                 else if ((TEXTREC->selStart >= cmdStart) && (TEXTREC->selStart < TEXTREC->teLength)) {
  522.                     TEDeactivate (hTERec);
  523.                     SetSelection (TEXTREC->selStart, TEXTREC->selStart + 1);
  524.                     TEDelete (hTERec);
  525.                     if (FrontWindow () == commandWin) TEActivate (hTERec);
  526.                 }
  527.                 break;
  528.             case CLRKEY:
  529.                 if (TEXTREC->selStart != TEXTREC->selEnd) DeleteRange ();
  530.                 break;
  531.             case DELETE:
  532.                 if (TEXTREC->selStart != TEXTREC->selEnd) DeleteRange ();
  533.                 else if (TEXTREC->selStart > cmdStart) {
  534.                     TEXTREC->clikStuff = 255;  /* to make sure the caret appears where it should */
  535.                     TEKey (DELETE, hTERec);
  536.                 }
  537.                 break;
  538.             case RETURN:
  539.                 if (wasOnLastLine) done = true;
  540.                 break;
  541.             case ENTER:  /* ENTER ends command no matter what */
  542.                 done = true;
  543.                 break;
  544.             default:
  545.                 DoStyle (boldStyle);
  546.                 TEXTREC->clikStuff = 255;  /* to make sure the caret appears where it should */
  547.                 TEKey (recentChar, hTERec);
  548.                 if ((recentChar == ')') && (TEXTREC->selStart > cmdStart)) {
  549.                     short parenCount = -1;
  550.                     Boolean inQuotes = false;
  551.                     i = TEXTREC->selStart - 1;
  552.                     while ((--i >= cmdStart) && (parenCount != 0))
  553.                         switch ((*TEXTREC->hText)[i]) {
  554.                         case DBLQUOTE: inQuotes = !inQuotes; break;
  555.                         case '(': if (!inQuotes) parenCount++; break;
  556.                         case ')': if (!inQuotes) parenCount--; break;
  557.                         }
  558.                     if (parenCount == 0) {
  559.                         cursorBeforeFlash = TEXTREC->selStart;
  560.                         SetSelection (i+1, i+2);  /* flash the matching open-paren */
  561.                         flashTime = 100;
  562.                     }
  563.                 } else if ((recentChar == DBLQUOTE) && (TEXTREC->selStart > cmdStart)) {
  564.                     i = TEXTREC->selStart - 1;
  565.                     while ((--i >= cmdStart) && ((*TEXTREC->hText)[i] != DBLQUOTE)) ;
  566.                     if ((*TEXTREC->hText)[i] == DBLQUOTE) {
  567.                         cursorBeforeFlash = TEXTREC->selStart;
  568.                         SetSelection (i, i+1);  /* flash the matching double-quote */
  569.                         flashTime = 100;
  570.                     }
  571.                 }
  572.             }
  573.             AdjustScrollBar ();
  574.         }
  575.     } while (!done);
  576. }
  577.  
  578. char *macgets (void) {
  579.     /* retrieve a typed character */
  580.     /* Note that this uses some extensive (and clever, if I may say so myself) buffering. */
  581.     int i, b, bufSize;
  582.     char *ptr, *buffer;
  583.     Boolean done, onLastLine;
  584.     
  585.     PrepareForInput ();
  586.     do {  /* repeat until a full expression has been typed */
  587.         EditFreely ();  /* allow free editing for a while */
  588.  
  589.         /* Now, we have a complete command to parse, if and only if: */
  590.         /* - the cursor was on the last line when the user pressed Return or Enter, and */
  591.         /* - the user either pressed Enter, or else every '(' since the beginning */
  592.         /*     of the command is matched by a ')'. */
  593.         /* Quoting is watched for.  ( ") is not a complete expression. */
  594.  
  595.         done = true;
  596.         if (TEXTREC->selStart != TEXTREC->teLength)  /* if we're not at the end already */
  597.             SetSelection (TEXTREC->teLength, TEXTREC->teLength);  /* send cursor to end */
  598.         TEXTREC->clikStuff = 255;  /* to make sure the caret appears where it should */
  599.         TEKey ('\r', hTERec);
  600.  
  601.         /* check and see if we've completed the command yet */
  602.         if (recentChar != ENTER) {
  603.             Boolean inQuotes = false;
  604.             short parenCount = 0;
  605.             for (i = cmdStart; i < TEXTREC->teLength; i++)
  606.                 switch ((*TEXTREC->hText)[i]) {
  607.                 case DBLQUOTE: inQuotes = !inQuotes; break;
  608.                 case '(': if (!inQuotes) parenCount++; break;
  609.                 case ')': if (!inQuotes) parenCount--; break;
  610.                 }
  611.             if ((parenCount > 0) || inQuotes) done = false;
  612.         }
  613.  
  614.         AdjustScrollBar ();
  615.     } while (!done);
  616.  
  617.     /* put the entire command into the buffer, and return it */
  618.     bufSize = TEXTREC->teLength - cmdStart;
  619.     buffer = (char *) NewPtr (bufSize + 1);
  620.     BlockMove (*TEXTREC->hText + cmdStart, buffer, bufSize);
  621.     buffer[bufSize] = '\0';
  622.     return buffer;
  623. }
  624.  
  625. void macputc (int ch) {
  626.     /* put a char into the output buffer, and flush the buffer if necessary */
  627.     switch (ch) {
  628.         case '\t':
  629.             do { macputc (' '); } while (cursorPos & 7);
  630.             break;
  631.         case DELETE:
  632.             if (cursorPos) cursorPos--;  /* and fall through to default */
  633.         default:
  634.             if (outputBufferLength == MAX_BUF) FlushOutput ();
  635.             if (ch == '\n') {
  636.                 cursorPos = 0;
  637.                 (*hOutputBuffer)[outputBufferLength++] = '\r';
  638.             } else {
  639.                 cursorPos++;
  640.                 (*hOutputBuffer)[outputBufferLength++] = ch;
  641.             }
  642.     }
  643. }
  644.  
  645. void macputs (char *s) {
  646.     /* for completeness */
  647.     while (*s) macputc (*s++);
  648. }
  649.  
  650. void scrflush (void) {
  651.     extern void osflush (void);
  652.     /* clear out everything */
  653.     FlushOutput ();
  654.     osflush ();
  655. }
  656.  
  657. void scrclear (void) {
  658.     /* clear text window -- not implemented */
  659. }
  660.  
  661. static void DoAppleMenu (int theItem) {
  662.     switch (theItem) {
  663.         case ABOUT_ITEM:
  664.             DoAboutBox ();
  665.             break;
  666.         default: {
  667.             Str255 name;
  668.             GetItem (appleMenu, theItem, name);
  669.             OpenDeskAcc (name);
  670.             break;
  671.         }
  672.     }
  673. }
  674.  
  675. static void DoFileMenu (int theItem) {
  676.     extern xlload (char *, int, int);
  677.     extern wrapup (void);
  678.     SFReply theFile;
  679.     SFTypeList fileTypes;
  680.     Point pt = { 100, 100 };
  681.  
  682.     fileTypes[0] = 'TEXT';
  683.     switch (theItem) {
  684.         case LOAD:
  685.         case LOAD_NOISILY:
  686.             StopPasting ();
  687.             SFGetFile (pt, NIL, NIL, 1, fileTypes, NIL, &theFile);
  688. /*            EraseBottomScroller (true);  /* SFGetFile puts it there */
  689.             if (theFile.good) {
  690.                 Str255 theFullPath;
  691.                 HiliteMenu (0);
  692.                 SetVol (NIL, theFile.vRefNum);
  693.                 SetSelection (TEXTREC->teLength, TEXTREC->teLength);  /* send cursor to end */
  694.                 if ((xlload (PtoCstr (theFile.fName), 1, (theItem == LOAD_NOISILY))) == 0)
  695.                     xlabort ("load error");
  696.                 macputs ("> ");
  697.                 PrepareForInput ();
  698.             }
  699.             break;
  700.         case QUIT:
  701.             wrapup ();
  702.     }
  703. }
  704.  
  705. static void DoEditMenu (int theItem) {
  706.     if (SystemEdit (theItem-1) == false)
  707.         switch (theItem) {
  708.         case CUT: case COPY:
  709.             if (ZeroScrap () == noErr) {
  710.                 TECopy (hTERec);  /* after copying, export the TE scrap */
  711.                 if (TEToScrap () != noErr) ZeroScrap ();
  712.             }
  713.             if (theItem == CUT) DeleteRange ();
  714.             break;
  715.         case PASTE: {
  716.             long scrapOffset;
  717.             if (pastedTextH) DisposHandle (pastedTextH);
  718.             pastedTextH = (CharsHandle) NewHandle (0);
  719.             pastedLength = GetScrap (pastedTextH, 'TEXT', &scrapOffset);
  720.             if (pastedLength < 0) pastedLength = 0;  /* error */
  721.             else {
  722.                 SetHandleSize (pastedTextH, pastedLength + 1);
  723.                 HLock (pastedTextH);
  724.                 ((char *)(*pastedTextH))[pastedLength] = '\0';
  725.                 HUnlock (pastedTextH);
  726.                 }
  727.             }  /* and fall through ... */
  728.         case CLEAR:
  729.             DeleteRange ();
  730.             break;
  731.         }
  732. }
  733.  
  734. static void DoControlMenu (int theItem) {
  735.     extern xlbreak (char *, char *);
  736.     extern char *s_unbound;
  737.     extern xlcontinue (void);
  738.     extern xlcleanup (void);
  739.     extern xlabort (char *);
  740.     extern xltoplevel (void);
  741.  
  742.     scrflush ();
  743.     HiliteMenu (0);
  744.     switch (theItem) {
  745.         case BREAK:            StopPasting ();    xlbreak ("user break", s_unbound);    PrepareForInput ();    break;
  746.         case CONTINUE:        StopPasting ();    xlcontinue ();                        PrepareForInput ();    break;
  747.         case CLEAN_UP:        StopPasting ();    xlcleanup ();                        PrepareForInput ();    break;
  748.         case CANCEL_INPUT:    StopPasting ();    xlabort ("input canceled");            PrepareForInput ();    break;
  749.         case TOP_LEVEL:        StopPasting ();    xltoplevel ();                        PrepareForInput ();    break;
  750.         case SHOW_GRAPHICS:
  751.             if (graphicsShown) HideGrafWin ();
  752.             else ShowGrafWin ();
  753.             break;
  754.         case SPLIT_SCREEN:
  755.             MoveWindow (commandWin, sHorizontal, sVertical, -1);
  756.             SizeWindow (commandWin, sWidth, sHeight, -1);
  757.             InvalRect (&commandWin->portRect);
  758.             SetTERect ();
  759.             SetScrollRect ();
  760.             ShowGrafWin ();
  761.             MoveWindow (graphicsWin, gHorizontal, gVertical, -1);
  762.             SizeWindow (graphicsWin, gWidth, gHeight, -1);
  763.             break;
  764.     }
  765. }
  766.  
  767. static void DoMenu (long choice) {
  768.     int theMenu = HiWord (choice), theItem = LoWord (choice);
  769.  
  770.     HiliteMenu (theMenu);
  771.     switch (theMenu) {
  772.         case APPLE_MENU_RES:    DoAppleMenu (theItem);        break;
  773.         case FILE_MENU_RES:        DoFileMenu (theItem);        break;
  774.         case EDIT_MENU_RES:        DoEditMenu (theItem);        break;
  775.         case CONTROL_MENU_RES:    DoControlMenu (theItem);    break;
  776.     }
  777.     HiliteMenu (0);
  778. }
  779.  
  780. static void AdjustMenus (void) {
  781.     /* turn the stuff in the Edit menu on and off as necessary */
  782.     long temp;
  783.     DisableItem (editMenu, UNDO);
  784.     if (TEXTREC->selStart != TEXTREC->selEnd) {
  785.         EnableItem (editMenu, CUT);
  786.         EnableItem (editMenu, COPY);
  787.         EnableItem (editMenu, CLEAR);
  788.     } else {
  789.         DisableItem (editMenu, CUT);
  790.         DisableItem (editMenu, COPY);
  791.         DisableItem (editMenu, CLEAR);
  792.     }
  793.     if (GetScrap (NIL, 'TEXT', &temp) > 0) EnableItem (editMenu, PASTE);
  794.     else DisableItem (editMenu, PASTE);
  795. }
  796.  
  797. static void DoContent (void) {
  798.     /* handle a click in a window's content region */
  799.     ControlHandle theScrollBar;
  800.     GrafPtr oldPort;
  801.     int scrollValue;
  802.     Point mouse = theEvent.where;
  803.     int thePart;
  804.  
  805.     GetPort (&oldPort);
  806.     SetPort (commandWin);
  807.     GlobalToLocal (&mouse);
  808.     
  809.     if (thePart = FindControl (mouse, commandWin, &theScrollBar))
  810.         switch (thePart) {
  811.             case inUpButton:
  812.             case inDownButton:
  813.             case inPageUp:
  814.             case inPageDown:
  815.                 scrollValue = TrackControl (theScrollBar, mouse, (ProcPtr) ScrollProc);
  816.                 break;
  817.             case inThumb:
  818.                 scrollValue = GetCtlValue (theScrollBar);
  819.                 thePart = TrackControl (theScrollBar, mouse, NIL);
  820.                 if (thePart) {
  821.                     scrollValue -= GetCtlValue (theScrollBar);
  822.                     if (scrollValue) TEScroll (0, scrollValue * LINEHEIGHT, hTERec);
  823.                 }
  824.                 break;
  825.         }
  826.  
  827.     else if (PtInRect (mouse, &(TEXTREC->viewRect)))
  828.         TEClick (mouse, (theEvent.modifiers & shiftKey) != 0, hTERec);
  829.  
  830.     SetPort (oldPort);
  831.  
  832. }
  833.  
  834. static void DoMouseDown (void) {
  835.     WindowPtr whichWindow;
  836.     short int thePart = FindWindow (theEvent.where, &whichWindow);
  837.  
  838.     switch (thePart) {
  839.         case inSysWindow:
  840.             SystemClick (&theEvent, whichWindow);
  841.             break;
  842.         case inDrag:
  843.             DragWindow (whichWindow, theEvent.where, &dragRect);
  844.             break;
  845.         case inMenuBar: {
  846.             long choice;
  847.             AdjustMenus ();
  848.             choice = MenuSelect (theEvent.where);
  849.             if (choice) DoMenu (choice);
  850.             break;
  851.         }
  852.         case inGoAway:
  853.             if ((whichWindow == graphicsWin)
  854.                     && (TrackGoAway (whichWindow, theEvent.where)))
  855.                 HideGrafWin ();
  856.             break;
  857.         case inContent:
  858.             if ((FrontWindow () == commandWin) && (whichWindow == commandWin))
  859.                 DoContent ();
  860.             else SelectWindow (whichWindow);
  861.             break;
  862.         case inGrow:
  863.         case inZoomIn:
  864.         case inZoomOut: {
  865.             long newSize;
  866.             GrafPtr oldPort;
  867.             if (thePart == inGrow) newSize = GrowWindow (whichWindow, theEvent.where, &sizeRect);
  868.             if (((thePart == inGrow) && newSize)
  869.                 || ((thePart != inGrow) && TrackBox (whichWindow, theEvent.where, thePart))) {
  870.                 GetPort (&oldPort);
  871.                 SetPort (whichWindow);
  872.                 EraseRect (&whichWindow->portRect);
  873.                 if (thePart == inGrow) SizeWindow (whichWindow, LoWord (newSize), HiWord (newSize), -1);
  874.                 else ZoomWindow (whichWindow, thePart, 0);
  875.                 commandWinResized = true;
  876.                 InvalRect (&whichWindow->portRect);
  877.                 SetPort (oldPort);
  878.             }
  879.             break;
  880.         }
  881.     }
  882. }
  883.  
  884. static void DoKeyPress (void) {
  885.     short whatKey = theEvent.message & charCodeMask;
  886.     if (theEvent.modifiers & cmdKey) {
  887.         long choice;
  888.         AdjustMenus ();
  889.         if (choice = MenuKey (theEvent.message)) DoMenu (choice);
  890.         else if (((whatKey == 'w') || (whatKey == 'W')) && (FrontWindow () == graphicsWin))
  891.             HideGrafWin ();
  892.         else if (whatKey == LEFTARROW)    GoStartOfLine ();
  893.         else if (whatKey == RIGHTARROW)    GoEndOfLine ();
  894.         else if (whatKey == UPARROW)    DoScrollBar (vScroll, -linesInView);
  895.         else if (whatKey == DOWNARROW)    DoScrollBar (vScroll, linesInView);
  896.     }
  897.     else if (theEvent.modifiers & optionKey) {
  898.         if (whatKey == LEFTARROW)        GoBackOneWord ();
  899.         else if (whatKey == RIGHTARROW)    GoForwardOneWord ();
  900.     }
  901.     else switch (whatKey) {
  902.         case PAGEUP:    DoScrollBar (vScroll, -linesInView);    break;
  903.         case PAGEDN:    DoScrollBar (vScroll, linesInView);        break;
  904.         case HOMEKEY:    DoScrollBar (vScroll, INT_MIN);            break;
  905.         case ENDKEY:    DoScrollBar (vScroll, INT_MAX);            break;
  906.         case FNKEY:                                                break;
  907.         case HELPKEY:                                            break;
  908.         default: recentChar = theEvent.message & charCodeMask;
  909.     }
  910. }
  911.  
  912. void DoEvent (void) {
  913.     if (wneImplemented) WaitNextEvent (everyEvent, &theEvent, NIL, NIL);
  914.     else {
  915.         SystemTask ();
  916.         GetNextEvent (everyEvent, &theEvent);
  917.     }
  918.  
  919.     AdjustCursor ();
  920.     if ((flashTime) && (--flashTime == 0)) SetSelection (cursorBeforeFlash, cursorBeforeFlash);
  921.     if (outputBufferLength) FlushOutput ();
  922.     if (FrontWindow () == commandWin) TEIdle (hTERec);
  923.     recentChar = '\0';
  924.  
  925.     if (howManyFiles) {
  926.         extern xlload (char *, int, int);
  927.         AppFile aFile;
  928.         GetAppFiles (++whichFile, &aFile);
  929.         if (whichFile == howManyFiles) {
  930.             howManyFiles = 0;
  931.             whichFile = 0;
  932.         }
  933.         SetVol (NIL, aFile.vRefNum);
  934.         PtoCstr (aFile.fName);
  935.         if (xlload ((char *) aFile.fName, 1, 0) == 0) xlabort ("load error");
  936.         ClrAppFiles (whichFile);
  937.         if (howManyFiles == 0) {
  938.             macputs ("> ");
  939.             PrepareForInput ();
  940.         }
  941.     }
  942.  
  943.     switch (theEvent.what) {
  944.         case mouseDown:
  945.             DoMouseDown ();
  946.             break;
  947.         case keyDown:
  948.         case autoKey:
  949.             DoKeyPress ();
  950.             break;
  951.         case activateEvt: {
  952.             WindowPtr whichWindow = (WindowPtr)theEvent.message;
  953.             SetPort (whichWindow);
  954.             if (whichWindow == commandWin) {
  955.                 DrawOnlyGrowIcon (commandWin);
  956.                 if ((theEvent.modifiers & activeFlag) == 1) {
  957.                     TEActivate (hTERec);
  958.                     HiliteControl (vScroll, 0);
  959.                 } else {
  960.                     TEDeactivate (hTERec);
  961.                     HiliteControl (vScroll, 255);
  962.                 }
  963.             }
  964.             break;
  965.         }
  966.         case updateEvt: {
  967.             if ((WindowPtr)theEvent.message == commandWin) UpdateCmdWindow ();
  968.             break;
  969.         }
  970.     }
  971. }
  972.  
  973. void MacWrapUp (void) {
  974.     /* take everything down in preparation for quitting */
  975.     StopPasting ();
  976.     CloseWindow (graphicsWin);
  977.     CloseWindow (commandWin);
  978.     TEDispose (hTERec);
  979.     DisposHandle (hOutputBuffer);
  980. }
  981.  
  982.